#pragma once

#include "Vertex.hpp"

class VertexArray {

	friend class Mesh;
	
private:
	
	Vertex* data = 0;

	unsigned int size = 0;

	GLuint vao;

	GLuint vbo;
	
	void draw();
	
	void bind();

public:

	void resize(unsigned int size);
	
	const unsigned int getSize() const;
	
	Vertex* map(GLenum access = GL_READ_WRITE);
	
	GLboolean unmap();
	
	Vertex* getDataPtr();
	
	const GLboolean isMapped() const;
	
private:
	
	void save(FILE* f);
	
	void load(FILE* f);
	
public:

	VertexArray();
	
	~VertexArray();
	
};

void VertexArray::save(FILE* f){
	if (data)
		unmap();
	fwrite(&size,sizeof(size),1,f);
	map(GL_READ_ONLY);
	fwrite(data,sizeof(Vertex),size,f);
	unmap();
}
	
void VertexArray::load(FILE* f){
	if (data)
		unmap();
	unsigned int sz;
	if (fread(&sz,sizeof(sz),1,f)!=1){
		printf("file read error\n");
		exit(1);
	}
	resize(sz);
	map(GL_WRITE_ONLY);
	if (fread(data,sizeof(Vertex),size,f) != (unsigned int)size){
		printf("file read error\n");
		exit(1);
	}
	unmap();	
}

Vertex* VertexArray::getDataPtr(){
	return data;
}

const GLboolean VertexArray::isMapped() const{
	return data ? GL_TRUE : GL_FALSE;
}

const unsigned int VertexArray::getSize() const{
	return size;
}

GLboolean VertexArray::unmap(){
	if (data) {
		bind();
		data = 0;
		return glUnmapBuffer(GL_ARRAY_BUFFER);
	} else 
		return true;
}

Vertex* VertexArray::map(GLenum access){
	bind();
	data = (Vertex*)glMapBuffer(GL_ARRAY_BUFFER,access);
	return data;
}

void VertexArray::resize(unsigned int size){
	VertexArray::size = size;
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER,vbo);
	glBufferData(GL_ARRAY_BUFFER,size*sizeof(Vertex),0,GL_STATIC_DRAW);
}

void VertexArray::draw(){
	glBindVertexArray(vao);
	glDrawArrays(GL_TRIANGLES,0,size);
}

void VertexArray::bind(){
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER,vbo);
}

VertexArray::VertexArray(){
	glGenVertexArrays(1,&vao);
	glGenBuffers(1,&vbo);
	glBindVertexArray(vao);
	glBindBuffer(GL_ARRAY_BUFFER,vbo);
	glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(const GLvoid*)(0));
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(const GLvoid*)(sizeof(GLfloat)*3));
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,sizeof(Vertex),(const GLvoid*)(sizeof(GLfloat)*6));
	glEnableVertexAttribArray(2);
	glVertexAttribPointer(3,1,GL_UNSIGNED_INT,GL_FALSE,sizeof(Vertex),(const GLvoid*)(sizeof(GLfloat)*8));
	glEnableVertexAttribArray(3);
}

VertexArray::~VertexArray(){
	glDeleteBuffers(1,&vbo);
	glDeleteVertexArrays(1,&vao);
}

